from typing import *

from . import db
from .log import log


def get_filed_names(cur) -> List[str]:
    names = [des[0] for des in cur.description]
    return names


def row_to_obj(names, cols) -> Dict[str, any]:
    m = {}
    for k, v in zip(names, cols):
        m[k] = v
    return m


def rows_to_objs(names, rows) -> List[Dict[str, any]]:
    ms = []

    for cols in rows:
        m = {}
        for k, v in zip(names, cols):
            m[k] = v
        ms.append(m)

    return ms


def create_conn():
    conn = db.get_sql_client()
    # cursor = conn.cursor(pymysql.cursors.DictCursor)
    cursor = conn.cursor()
    return conn, cursor


def close_conn(conn, cursor):
    cursor.close()
    conn.close()


def select_one(sql, *args, **kwargs) -> Dict[str, any]:
    conn, cur = create_conn()
    cur = cur.execute(sql, *args, **kwargs)
    result = cur.fetchone()
    if result:
        names = get_filed_names(cur)
        result = row_to_obj(names, result)
    close_conn(conn, cur)
    return result


def executescript(sql: str) -> NoReturn:
    conn, cur = create_conn()
    cur.executescript(sql)
    close_conn(conn, cur)
    return


def select_all(sql, *args, **kwargs):
    conn, cur = create_conn()
    cur = cur.execute(sql, *args, **kwargs)
    result = cur.fetchall()
    names = get_filed_names(cur)
    close_conn(conn, cur)
    result = rows_to_objs(names, result)
    return result


def insert_one(sql, args):
    conn, cur = create_conn()
    result = cur.execute(sql, args)
    conn.commit()
    close_conn(conn, cur)
    return result


def delete_one(sql, args):
    conn, cur = create_conn()
    result = cur.execute(sql, args)
    conn.commit()
    close_conn(conn, cur)
    return result


def update_one(sql, args):
    conn, cur = create_conn()
    result = cur.execute(sql, args)
    conn.commit()
    close_conn(conn, cur)
    return result


def table_exists(tbl_name: str) -> bool:
    sql = f"select 1 from {tbl_name} limit 1"
    try:
        select_one(sql)
    except Exception:
        log.exception("unexpect")
        return False
    return True
